#ifndef YOOHOO_XMPPCORE_CONNECTION_H
#define YOOHOO_XMPPCORE_CONNECTION_H

#include "xmppcore/export.h"
#include "xmppcore/connectionconfig.h"
#include "xmppcore/presence.h"
#include "xmppcore/message.h"
#include "xmppcore/iq.h"
#include "xmppcore/stanzaerror.h"
#include "xmppcore/streamerror.h"
#include "xmppcore/negotiant.h"
#include "xmppcore/streamfeaturefactory.h"
#include "xmppcore/saslnegotiant.h"
#include "xmppcore/resourcebindingnegotiant.h"
#include "xmppcore/sessionnegotiant.h"

#include <QtCore/QObject>
#include <QtCore/QMutex>
#include <QtNetwork/QAbstractSocket>

namespace yoohoo_xmppcore {

class ConnectionListener;
class TlsCallback;
class Connector;
class InitialStreamNegotiant;
class TlsNegotiant;
class SaslNegotiant;
class ResourceBindingNegotiant;
class SessionNegotiant;
class StreamFeatures;

class YOOHOO_XMPPCORE_EXPORT Connection : public QObject {
	Q_OBJECT
public:
	enum XmppState {
		XmppStateUnconnected,
		XmppStateStreamNegotiating,
		XmppStateWaitForAuthenticating,
		XmppStateAuthenticated,
		XmppStateResourceBinded,
		XmppStateSessionEstablished
	};
	enum ConnectionError {
		NoError,

		// network errors
		ConnectionRefusedError,
		RemoteHostClosedError,
		HostNotFoundError,
		SocketAccessError,
		SocketResourceError,
		SocketTimeoutError,
		DatagramTooLargeError,
		NetworkError,
		AddressInUseError,
		SocketAddressNotAvailableError,
		UnsupportedSocketOperationError,
		UnfinishedSocketOperationError,
		ProxyAuthenticationRequiredError,
		SslHandshakeFailedError,
		ProxyConnectionRefusedError,
		ProxyConnectionClosedError,
		ProxyConnectionTimeoutError,
		ProxyNotFoundError,
		ProxyProtocolError,
		UnknownSocketError,

		// negotiation errors
		UnknownNegotiationProtocolError,
		UnknownNegotiationError,

		// sasl errors
		SaslNotSupportedError,
		SaslInitError,
		SaslHandshakeError,
		SaslCryptError,
		UnknownSaslError,

		// protocol error
		UnknownProtoclError
	};
	enum ConnectionState {
		UnconnectedState,
		HostLookupState,
		ConnectingState,
		ConnectedState,
		BoundState,
		ListeningState,
		ClosingState,
		UnknownState
	};
	enum AuthFailure {
		AuthFailureAborted,
		AuthFailureIncorrectEncoding,
		AuthFailureInvalidAuthzid,
		AuthFailureInvalidMechanism,
		AuthFailureMechanismTooWeak,
		AuthFailureNotAuthorized,
		AuthFailureTemporaryAuthFailure,
	};
	enum ResourceBindingError {
		BindingErrorConflict,
		BindingErrorNotAllowed,
		BindingErrorForbidden
	};
	enum SessionEstablishmentError {
		SessionEstablishmentErrorInternalServer,
		SessionEstablishmentErrorForbidden,
		SessionEstablishmentErrorConflict
	};
	struct StreamFeaturePair {
		QString name;
		QString namespaceUri;
	};
	Connection(ConnectionConfig& config, QObject* parent = 0);
	~Connection();
	static void write(QString xml, Connection* connection);
	static void sendMessage(Message message, Connection* connection);
	static void sendPresence(Presence presence, Connection* connection);
	static void sendIq(Iq iq, Connection* connection);

signals:
	void connectionError(Connection::ConnectionError error);
	void connectionState(Connection::ConnectionState state);
	void readyToAuthenticate();
	void authenticated();
	void authFailure(Connection::AuthFailure failure);
	void resourceBindingError(Connection::ResourceBindingError error);
	void resourceBinded(QString jid);
	void sessionEstablished();
	void sessionEstablishmentError(SessionEstablishmentError error);
	void streamClosed();
	void messageReceived(Message message);
	void presenceReceived(Presence presence);
	void iqReceived(Iq iq);
	void written(QString xml);
	void received(QString xml);
	void streamError(StreamError streamError);
	void stanzaError(StanzaError stanzaError);

public slots:
	void changeThread(QThread* thread);
	void connectToHost();
	void disconnectFromHost();
	void authenticate(QString userName, QString password);
	void write(QString xml);
	void sendMessage(Message message);
	void sendPresence(Presence presence);
	void sendIq(Iq iq);
	void setTlsCallback(TlsCallback* tlsCallback);
	bool isClientTlsRequired();
	XmppState xmppState();
	Connection::ConnectionState connectionState();
	Connection::ConnectionError connectionError();
	void registerStreamFeature(StreamFeatureFactory* streamFeatureFactory);
	bool isStreamFeatureSupported(QString name, QString namespaceUri);
	QString bindedJid();

private slots:
	void processSocketError(QAbstractSocket::SocketError error);
	void processSocketStateChanged(QAbstractSocket::SocketState state);
	void processSocketReady();
	void processWritten(QString xml);
	void processReceived(QString xml);

	void processInitialStreamDone(StreamFeatures* streamFeatures);
	void processTlsDone(StreamFeatures* streamFeatures);
	void processSaslDone(StreamFeatures* streamFeatures);
	void processSaslSuccess();
	void processSaslError(SaslNegotiant::SaslError error);
	void processResourceBindingSuccess(QString jid);
	void processResourceBindingError(ResourceBindingNegotiant::ResourceBindingError error);
	void processSessionEstablishementSuccess();
	void processSessionEstablishmentError(SessionNegotiant::SessionEstablishmentError error);
	void processNegotiationError(Negotiant::NegotiationError error);

private:
	void registerStreamFeatureFromFactory(Negotiant* negotiant,
			StreamFeatureFactory::FeatureAdvertisementPhase phase);
	void createConnector();
	void destroyNegotiants();
	void saveSupportedStreamFeatures(Negotiant* negotiant);

private:
	Connector* _connector;
	bool _isClientTlsRequired;
	TlsCallback* _tlsCallback;
	InitialStreamNegotiant* _initialStreamNegotiant;
	TlsNegotiant* _tlsNegotiant;
	SaslNegotiant* _saslNegotiant;
	ResourceBindingNegotiant* _resourceBindingNegotiant;
	SessionNegotiant* _sessionNegotiant;
	XmppState _xmppState;
	ConnectionState _connectionState;
	ConnectionError _connectionError;
	Server* _server;
	QString _resource;
	QList<StreamFeatureFactory*> _streamFeatureFactories;
	bool _tlsRequired;
	QList<StreamFeaturePair> _supportedStreamFeatures;
	bool _closeStreamSignalReceived;
	QString _bindedJid;
	XmppParsingFactory* _parsingFactory;
};

}

#endif
